﻿//-----------------------------------------------------------------------
// <copyright file="ShelveUpdate.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation 2011. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Internal.TeamFoundationServer.Submit
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;
    using Microsoft.TeamFoundation;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Properties;

    /// <summary>
    /// ShelveUpdate class.
    /// </summary>
    internal partial class ShelveUpdate : WizardControlTemplate
    {
        /// <summary>
        /// Shelve set dictionary.
        /// </summary>
        private Dictionary<string, Shelveset> shelvesets;

        /// <summary>
        /// Initializes a new instance of the <see cref="ShelveUpdate"/> class.
        /// </summary>
        public ShelveUpdate()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.InitializeComponent();

                this.cmbTFSServer.Items.Clear();
                foreach (var item in TeamFoundationHelper.GetRegisteredCollectionNames())
                {
                    this.cmbTFSServer.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbTFSServer);

                if (this.cmbTFSServer.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbTFSServer, Settings.Default.TfsSourceServer);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Update window.
        /// </summary>
        /// <param name="title">Window Title.</param>
        /// <param name="description">Window Description.</param>
        /// <param name="backButtonEnabled">Back Button Enabled?</param>
        /// <param name="nextButtonEnabled">Next Button Enabled?</param>
        /// <param name="nextButtonCaption">Next Button Caption.</param>
        /// <param name="action">Action to perform on control.</param>
        public override void UpdateWindow(Label title, RichTextBox description, out bool backButtonEnabled, out bool nextButtonEnabled, out string nextButtonCaption, object action)
        {
            backButtonEnabled = true;
            nextButtonEnabled = this.gridFiles.SelectedRows.Count > 0;
            nextButtonCaption = Resources.UpdateShelveButtonCaption;

            title.Text = Resources.UpdateShelveTitle;
            description.Text = Resources.UpdateShelveDescription;
        }

        /// <summary>
        /// Button next method.
        /// </summary>
        public override void NextStep()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                if (!this.InternalValidate())
                {
                    return;
                }

                var workspace = TeamFoundationHelper.GetWorkspace(this.cmbTFSServer.Text, this.cmbWorkspace.Text);

                try
                {
                    var shelveset = new Shelveset(workspace.VersionControlServer, this.cmbShelveset.Text, workspace.VersionControlServer.AuthorizedUser)
                    {
                        Comment = this.textDescription.Text
                    };

                    var changes = this.GetSelectedPendingChanges();

                    try
                    {
                        workspace.Shelve(shelveset, changes, ShelvingOptions.Replace);
                    }
                    catch (InvalidPathException err)
                    {
                        MessageBox.Show(
                            err.Message,
                            Resources.ErrorString,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error,
                            MessageBoxDefaultButton.Button1,
                            MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                        return;
                    }

                    MessageBox.Show(
                        string.Format(CultureInfo.InvariantCulture, Resources.ShelveSetUpdatedString, shelveset.Name),
                        Resources.InformationString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                    this.FireGoHomeRequested(WizardMainMenuControl.MainMenuAction.ValidateChanges /* after shelve navigate to validate changes */);
                }
                catch (VersionControlException err)
                {
                    MessageBox.Show(
                        err.Message,
                        Resources.ErrorString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                }
                catch (FileNotFoundException err)
                {
                    MessageBox.Show(
                       err.Message,
                       Resources.ErrorString,
                       MessageBoxButtons.OK,
                       MessageBoxIcon.Information,
                       MessageBoxDefaultButton.Button1,
                       MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                }
                catch (IOException err)
                {
                    MessageBox.Show(
                        err.Message,
                        Resources.ErrorString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select TFS Collection.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbTfsServerSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbWorkspace.Items.Clear();
                this.cmbShelveset.Items.Clear();
                this.gridFiles.Rows.Clear();
                this.textProject.Text = string.Empty;

                if (this.cmbTFSServer.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceServer", this.cmbTFSServer.Text);

                // Retrieve User workspaces for specific project collection.
                foreach (var item in TeamFoundationHelper.GetWorkspaceNames(this.cmbTFSServer.Text))
                {
                    this.cmbWorkspace.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbWorkspace);

                if (this.cmbWorkspace.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbWorkspace, Settings.Default.TfsSourceWorkspace);
                }
            }
            catch (TeamFoundationServiceUnavailableException err)
            {
                MessageBox.Show(
                   err.Message,
                   Resources.UnhandledException,
                   MessageBoxButtons.OK,
                   MessageBoxIcon.Error,
                   MessageBoxDefaultButton.Button1,
                   MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select Workspace.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbWorkspaceSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbShelveset.Items.Clear();
                this.gridFiles.Rows.Clear();
                this.textDescription.Text = string.Empty;
                this.textProject.Text = string.Empty;
                this.shelvesets = null;

                if (this.cmbWorkspace.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceWorkspace", this.cmbWorkspace.Text);

                this.shelvesets = TeamFoundationHelper.GetShelvesets(this.cmbTFSServer.Text, this.cmbWorkspace.Text).ToDictionary(obj => obj.Name, obj => obj);
                foreach (var shelveset in this.shelvesets)
                {
                    this.cmbShelveset.Items.Add(shelveset.Value.Name);
                }

                Utilities.FixComboItemsWidth(this, this.cmbShelveset);

                if (this.cmbShelveset.Items.Count > 0)
                {
                    this.cmbShelveset.SelectedIndex = 0;
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Get Selected PendingChanges.
        /// </summary>
        /// <returns>List of Pending Changes.</returns>
        private PendingChange[] GetSelectedPendingChanges()
        {
            var pendingChanges = new List<PendingChange>();
            for (var index = this.gridFiles.Rows.Count - 1; index >= 0; index--)
            {
                if (!this.gridFiles.Rows[index].Visible || !bool.Parse(this.gridFiles.Rows[index].Cells["colSelect"].Value.ToString()))
                {
                    continue;
                }

                var pendingChange = this.gridFiles.Rows[index].Cells["colItem"].Value as PendingChange;
                if (pendingChange != null)
                {
                    pendingChanges.Add(pendingChange);
                }
            }

            return pendingChanges.ToArray();
        }

        /// <summary>
        /// Internal Validate
        /// </summary>
        /// <returns>False if has errors, otherwise true.</returns>
        private bool InternalValidate()
        {
            this.errorProvider.Clear();
            var hasErrors = false;

            var hasFiles = this.gridFiles.Rows.Cast<DataGridViewRow>().Any(row => bool.Parse(row.Cells["colSelect"].Value.ToString()));
            if (!hasFiles)
            {
                hasErrors = true;
                MessageBox.Show(
                       Resources.NoFilesSelectedToShelveError,
                       Resources.ErrorString,
                       MessageBoxButtons.OK,
                       MessageBoxIcon.Information,
                       MessageBoxDefaultButton.Button1,
                       MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }

            if (this.textDescription.TextLength == 0)
            {
                this.errorProvider.SetError(this.lblDescription, Resources.NoShelvesetDescriptionProvided);
                hasErrors = true;
            }

            return !hasErrors;
        }

        /// <summary>
        /// Change Shelve set event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbShelvesetSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.gridFiles.Rows.Clear();
                this.textDescription.Text = string.Empty;
                this.textProject.Text = string.Empty;

                if (this.cmbShelveset.Text.Length == 0)
                {
                    return;
                }

                this.textDescription.Text = this.shelvesets[this.cmbShelveset.Text].Comment;
                
                var projects = new List<string>();
                var pendingChanges = new List<PendingChange>(TeamFoundationHelper.GetPendingChangesByShelveSetName(this.cmbTFSServer.Text, this.cmbWorkspace.Text, this.cmbShelveset.Text)).ToDictionary(obj => obj.ServerItem, obj => obj);
                foreach (var pendingChange in pendingChanges)
                {
                    var localOrServerItem = pendingChange.Value.LocalOrServerItem;

                    var projectName = TeamFoundationHelper.GetTeamProjectNameByLocalItem(this.cmbTFSServer.Text, this.cmbWorkspace.Text, localOrServerItem);
                    if (!string.IsNullOrEmpty(projectName) && !projects.Contains(projectName))
                    {
                        projects.Add(projectName);
                    }
                }

                if (projects.Count > 1)
                {
                    MessageBox.Show(
                       Resources.MultipleProjectsShelveSetError,
                       Resources.ErrorString,
                       MessageBoxButtons.OK,
                       MessageBoxIcon.Information,
                       MessageBoxDefaultButton.Button1,
                       MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                    return;
                }
                else
                {
                    this.textProject.Text = projects[0];
                }

                var allPendingChanges = new List<PendingChange>(TeamFoundationHelper.GetPendingChanges(this.cmbTFSServer.Text, this.cmbWorkspace.Text));
                foreach (var pendingChange in allPendingChanges)
                {
                    var projectName = TeamFoundationHelper.GetTeamProjectNameByLocalItem(this.cmbTFSServer.Text, this.cmbWorkspace.Text, pendingChange.LocalItem);
                    if (projectName != this.textProject.Text)
                    {
                        continue;
                    }

                    if (!TeamFoundationHelper.FileHasChanges(pendingChange))
                    {
                        continue;
                    }

                    var rowIndex = this.gridFiles.Rows.Add();
                    this.gridFiles.Rows[rowIndex].Cells["colProject"].Value = projectName;
                    this.gridFiles.Rows[rowIndex].Cells["colSelect"].Value = pendingChanges.ContainsKey(pendingChange.ServerItem);
                    this.gridFiles.Rows[rowIndex].Cells["colChange"].Value = pendingChange.ChangeTypeName;
                    this.gridFiles.Rows[rowIndex].Cells["colFileName"].Value = pendingChange.FileName;
                    this.gridFiles.Rows[rowIndex].Cells["colLocalPath"].Value = pendingChange.LocalItem;
                    this.gridFiles.Rows[rowIndex].Cells["colServerPath"].Value = pendingChange.ServerItem;
                    this.gridFiles.Rows[rowIndex].Cells["colItem"].Value = pendingChange;
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Header click event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void ChkRowsCheckedChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                // We use this code to mark/unmark all files as selected/unselected. 
                var dataGridViewColumn = this.gridFiles.Columns["colSelect"];
                if (dataGridViewColumn != null)
                {
                    var selected = this.chkRows.Checked;
                    for (var index = this.gridFiles.Rows.Count - 1; index >= 0; index--)
                    {
                        this.gridFiles.Rows[index].Cells["colSelect"].Value = selected;
                    }
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Grid Header column width changed event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridFilesColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column == this.gridFiles.Columns["colSelect"])
            {
                Debug.Assert(e.Column != null, "Column should never be null");
                e.Column.Width = 50;
            }
        }

        /// <summary>
        /// Files Selection change event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments.</param>
        private void GridFilesSelectionChanged(object sender, EventArgs e)
        {
            this.FireChangeContinueButtonEnabled(this.gridFiles.SelectedRows.Count > 0);
        }
    }
}
